#include "Model.h"


namespace SEngine
{    
    void Model::Transfrom(glm::mat4 model)
    {
        m_modelMatrix = model;
        // if (m_boundingGraphics) {
        //     m_boundingGraphics->Transfrom(model);
        // }
    }

    glm::mat4 Model::Transfrom()
    {
        return m_modelMatrix;
    }

    void Model::SetMeshFilter(ResRef<MeshFilter> meshes, ResRef<Material> material)
    {
        m_materials.clear();
        m_meshFilter = meshes;
        m_meshFilter.Value().Foreach([&](auto name, auto mesh) {
            m_materials.emplace(name, material);
        });
    }

    void Model::ForeachMaterialFilter(std::function<void(std::string, ResRef<Material> &)> &&fn)
    {
        for (auto & [name, material] : m_materials) {
            fn(name, material);
        }
    }

    void Model::SetMaterial(const std::string &name, ResRef<Material> material)
    {
        if (m_materials.find(name) != m_materials.end()) {
            m_materials[name] = material;
        }
    }

    void Model::Draw(Renderer &renderer)
    {
        if (m_meshFilter) {
            m_meshFilter.Value().Foreach([&](auto name, std::shared_ptr<Mesh> & mesh) {
                Drawable drawable;
                drawable.SetMesh(mesh);
                if (m_materials.at(name)) {
                    drawable.SetMaterial(m_materials.at(name));
                    drawable.SetAPIType(m_renderOpt.api);
                    drawable.SetInstanceCount(m_renderOpt.instanceCount);
                    drawable.SetMode(m_renderOpt.mode);
                    drawable.GetMaterial().Value().Set("u_model", m_modelMatrix);
                    drawable.GetMaterial().Value().Set("u_modelT", glm::transpose(glm::inverse(glm::mat3(m_modelMatrix))));
                    renderer.DrawEntity(drawable);
                }
            });
        }
    }
    
    void Model::Draw(Renderer &renderer, ResRef<Material> material)
    {
        if (m_meshFilter) {
            m_meshFilter.Value().Foreach([&](auto name, std::shared_ptr<Mesh> & mesh) {
                Drawable drawable;
                drawable.SetMesh(mesh);
                drawable.SetMaterial(material);
                drawable.SetAPIType(m_renderOpt.api);
                drawable.SetInstanceCount(m_renderOpt.instanceCount);
                drawable.SetMode(m_renderOpt.mode);
                drawable.GetMaterial().Value().Set("u_model", m_modelMatrix);
                drawable.GetMaterial().Value().Set("u_modelT", glm::transpose(glm::inverse(glm::mat3(m_modelMatrix))));
                renderer.DrawEntity(drawable);
            });
        }
    }

    void Model::Draw(Renderer & renderer, ResRef<Shader> shader)
    {
        if (m_meshFilter) {
            m_meshFilter.Value().Foreach([&](auto name, std::shared_ptr<Mesh> & mesh) {
                Drawable drawable;
                drawable.SetMesh(mesh);
                if (m_materials.at(name)) {
                    drawable.SetMaterial(m_materials.at(name));
                    auto oldShader = drawable.GetMaterial().Value().GetShader();
                    drawable.GetMaterial().Value().SetShader(shader);
                    drawable.SetAPIType(m_renderOpt.api);
                    drawable.SetInstanceCount(m_renderOpt.instanceCount);
                    drawable.SetMode(m_renderOpt.mode);
                    drawable.GetMaterial().Value().Set("u_model", m_modelMatrix);
                    drawable.GetMaterial().Value().Set("u_modelT", glm::transpose(glm::inverse(glm::mat3(m_modelMatrix))));
                    renderer.DrawEntity(drawable);
                    drawable.GetMaterial().Value().SetShader(oldShader);
                }
            });
        }
    }

    void Model::Reset() 
    {
        // m_boundingGraphics.reset();
        m_meshFilter.Reset();
        m_materials.clear();
        m_modelMatrix = glm::mat4{1.0f};
        m_renderOpt = RenderOption{};
    }

} // namespace SEngine
